Skip to content

retirement_createとtraining_completion_createの呼び出しをNewspaperからActiveSupport::Notificationsに移行#9219

Merged
komagata merged 13 commits intomainfrom
chore/replace-newspaper-with-activesupport-notifications-for-retirement-create
Jan 6, 2026
Merged

retirement_createとtraining_completion_createの呼び出しをNewspaperからActiveSupport::Notificationsに移行#9219
komagata merged 13 commits intomainfrom
chore/replace-newspaper-with-activesupport-notifications-for-retirement-create

Conversation

@matuaya
Copy link
Copy Markdown
Contributor

@matuaya matuaya commented Sep 28, 2025

Issue

概要

retirement_createの通知処理をNewspaperからActiveSupport::Notificationsの仕組みに移行しました。

変更確認方法

  1. chore/replace-newspaper-with-activesupport-notifications-for-retirement-createをローカルに取り込む
  2. foreman start -f Procfile.devでサーバーを起動する
  3. ユーザーが退会した時、ユーザーのWIP日報が削除されることを以下の手順で確認
  • hajimeでログインする。
  • 日報を作成しWIPとして保存する。
  • http://localhost:3000/reportsのページに行き、先ほど作成した日報が表示されていることを確認する。
  • Meメニューから退会手続きを完了する。
  • komagataでログインし、http://localhost:3000/reportsのページに行き、WIPの日報が削除されていることを確認する。
  1. 研修生が研修終了した時、研修生のWIP日報が削除されることを以下の手順で確認
  • kenshuでログインする。
  • 日報を作成しWIPとして保存する。
  • http://localhost:3000/reportsのページに行き、先ほど作成した日報が表示されていることを確認する。
  • Meメニューから研修終了手続きを完了する。
  • komagataでログインし、http://localhost:3000/reportsのページに行き、WIPの日報が削除されていることを確認する。
  1. アドミンがユーザーが退会させた時、ユーザーのWIP日報が削除されることを以下の手順で確認
  • tomomiでログインし、日報を作成しWIPとして保存する。
  • komagataでログインし、http://localhost:3000/reportsのページに行き、先ほど作成した日報が表示されていることを確認する。
  • ユーザー名をクリックし、tomomiのユーザーページに移動する(http://localhost:3000/users/350839102)。
  • ユーザーページの右下の項目「ステータス変更」の「管理者として情報変更」をクリックする。
  • ページをスクロールし、「以下管理者のみ操作ができます」にある項目、「ユーザーステータス」の「退会済」の文字をクリックし退会日を選択する。
  • さらにスクロールし、ページ下の「更新する」をクリックし、ユーザーを退会させる。
  • http://localhost:3000/reportsのページに行き、WIPの日報が削除されていることを確認する。

Screenshot

UIに変化はないためスクリーンショットは省略しています。

Summary by CodeRabbit

リリースノート

  • リファクタリング
    • イベント通知基盤を切替え、関連する購読者構成を整理しました(通知の発行・購読方式の変更)。
  • 機能変更
    • 未完成データ削除処理の実行後にユーザーのキャリアパスが初期状態へリセットされます。
  • テスト
    • データクリーンアップ処理のテストを追加・更新し、対象データの選別と通知連携を検証しています。

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Sep 28, 2025

Walkthrough

コントローラ/モデルの Newspaper.publish を ActiveSupport::Notifications.instrument に置換し、通知リスナ(UnfinishedDataDestroyer / TimesChannelDestroyer)を ActiveSupport::Notifications の5引数コールバック形式に合わせて購読・シグネチャ更新。UnfinishedDataDestroyer は処理後にユーザの career_path を 0 に設定する追加入力あり。

Changes

Cohort / File(s) 変更内容
イベント発行の移行
app/controllers/training_completion_controller.rb, app/models/retirement.rb
Newspaper.publish(...)ActiveSupport::Notifications.instrument('...', user: ...) に差し替え(イベント名をシンボル→文字列へ)。
サブスクライバ:シグネチャ更新
app/models/times_channel_destroyer.rb, app/models/unfinished_data_destroyer.rb
#call(payload)#call(_name, _started, _finished, _id, payload)(ActiveSupport::Notifications の5引数)へ変更。
サブスクライバ:追加ロジック
app/models/unfinished_data_destroyer.rb
既存の未完了データ破棄処理の後に user.update(career_path: 0) を追加。
初期化子:購読移行
config/initializers/newspaper.rb, config/initializers/active_support_notifications.rb
UnfinishedDataDestroyer / TimesChannelDestroyer の購読を Newspaper 初期化子から ActiveSupport::Notifications 初期化子へ移行。retirement.createtraining_completion.create を購読。
テストの更新/追加
test/models/times_channel_destroyer_test.rb, test/models/unfinished_data_destroyer_test.rb, test/models/retirement_test.rb
新しい5引数シグネチャに合わせたテスト更新/追加。UnfinishedDataDestroyer の削除条件と career_path リセット、Retirement の publish 呼び出し名の変更を検証。

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Controller as Controller (Training/Retirement)
    participant ASNotif as ActiveSupport::Notifications
    participant Unfinished as UnfinishedDataDestroyer
    participant Times as TimesChannelDestroyer
    participant Other as 既存購読者 (QuestionNotifier 等)

    Controller->>ASNotif: instrument('training_completion.create' / 'retirement.create', user: user)
    ASNotif-->>Unfinished: call(_name,_started,_finished,_id,payload)
    Unfinished->>Unfinished: 指定ユーザの未チェック製品・WIPレポートを削除
    Unfinished->>Unfinished: user.update(career_path: 0)
    ASNotif-->>Times: call(_name,_started,_finished,_id,payload)
    Times->>Times: times channel 関連破棄処理
    ASNotif-->>Other: 他の購読者へ通知
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 分

  • 注目箇所:
    • app/models/unfinished_data_destroyer.rb(career_path を 0 にする意図と副作用)
    • 初期化子(購読の重複や既存購読者との順序/競合)
    • テストの置換(5引数シグネチャに伴う呼び出しと期待値)

Possibly related issues

Possibly related PRs

Suggested reviewers

  • yokomaru
  • komagata

Poem

🐇 ぽんと跳ねて通知は変わる
古い新聞はそっとしまわれて
イベントは軽やかに波を描き
未完のものは消えて道はゼロへ戻る
ぴょん、祝おう — 小さな切替の夜🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed タイトルは、Newspaper gemからActiveSupport::Notificationsへの移行という主要な変更を明確に説明しており、変更内容とよく対応している。
Description check ✅ Passed PR説明はテンプレートのすべての必須セクション(Issue、概要、変更確認方法、Screenshot)を含み、詳細な確認手順とテスト内容が記載されている。
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/replace-newspaper-with-activesupport-notifications-for-retirement-create

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1c9eab and dc98428.

📒 Files selected for processing (2)
  • app/models/retirement.rb (2 hunks)
  • test/models/retirement_test.rb (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
test/**/*_test.rb

📄 CodeRabbit inference engine (AGENTS.md)

test/**/*_test.rb: Test suite should use Minitest with structure: system/, models/, controllers/, and fixtures in test/fixtures/; test files should be named *_test.rb
Use Minitest + Capybara for system tests; place unit and integration tests under matching test/* directories

Files:

  • test/models/retirement_test.rb
**/*.rb

📄 CodeRabbit inference engine (AGENTS.md)

Ruby code should use 2-space indentation, snake_case for method names, and CamelCase for class names, enforced by RuboCop (.rubocop.yml)

Files:

  • test/models/retirement_test.rb
  • app/models/retirement.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • test/models/retirement_test.rb
  • app/models/retirement.rb
test/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep tests deterministic and use fixtures stored in test/fixtures/ for test data

Files:

  • test/models/retirement_test.rb
test/**/*

⚙️ CodeRabbit configuration file

test/**/*: # Test

  • TestCase名は英語で書く。
  • どうしても避けられない時以外にsystem testでsleepは使わない。

Unit Test

model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。

Files:

  • test/models/retirement_test.rb
app/**/*.{rb,js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Rails app code should be organized in app/ directory with subdirectories: models/, controllers/, views/, jobs/, helpers/, and frontend code under javascript/ (Shakapacker)

Files:

  • app/models/retirement.rb
🧠 Learnings (3)
📓 Common learnings
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。
Learnt from: reckyy
Repo: fjordllc/bootcamp PR: 8356
File: app/models/notification.rb:45-47
Timestamp: 2025-07-15T12:58:02.735Z
Learning: fjordllc/bootcampプロジェクトの`app/models/notification.rb`において、`training_completed`通知種別は`TARGETS_TO_KINDS`ハッシュに含めない設計になっている。これは`training_completed`が通知一覧のフィルタ対象ではないためで、意図的な設計判断である。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:131-135
Timestamp: 2025-09-12T21:18:00.834Z
Learning: Rails アップグレードPRにおいて、product_review_not_completed メソッドの webhook URL 設定の shared フォールバック追加も、設定システム全体の変更として別PRで対応すべきである。
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/models/graduation_notifier.rb:23-28
Timestamp: 2025-09-12T21:16:47.639Z
Learning: Rails upgrade PRにおいて、configuration systemの変更やwebhook設定の改善提案も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/inquiry_notifier.rb`において、`NOTIFICATION_SENDER_NAME`で指定される管理者ユーザーは必ず存在することがシステムの前提条件として保証されているため、senderがnilになることは想定されておらず、エラーハンドリングは不要である。
Learnt from: sjabcdefin
Repo: fjordllc/bootcamp PR: 9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.036Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/work_notification_destroyer.rb`において、このクラスは`app/controllers/works_controller.rb`の`destroy`アクションから呼び出され、`before_action :set_my_work`でIDの妥当性チェックが事前に行われるため、payloadのworkオブジェクトは常に有効であることが保証されている。そのためエラーハンドリングは不要。
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: db/fixtures/buzzes.yml:0-0
Timestamp: 2025-09-04T01:27:23.422Z
Learning: The buzz_controller.rb was indeed deleted, but there are still two remaining items from the old buzz single page implementation that need cleanup: the data migration file db/data/20230726024528_create_buzz.rb and the old buzz route in config/routes.rb line 136. These should be updated/removed to complete the buzz page redesign migration.
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: db/fixtures/buzzes.yml:0-0
Timestamp: 2025-09-04T01:27:23.422Z
Learning: The buzz_controller.rb and related data migration files were deleted as part of the Buzz page redesign, not just modified. The old single buzz page was completely replaced with a new buzzes (plural) listing page with year-based navigation and metadata structure.
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 8977
File: app/controllers/reports_controller.rb:63-63
Timestamp: 2025-07-23T20:31:13.856Z
Learning: fjordllc/bootcampプロジェクトの`app/controllers/reports_controller.rb`において、`create`と`update`アクションは両方とも`report.save_uniquely`を使用し、同じ`:report_save`イベントと`'report.save'`イベントを発行する。これは両方とも本質的に「レポートの保存」操作であり、作成と更新を区別する必要がないためである。
📚 Learning: 2025-09-01T22:31:57.345Z
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。

Applied to files:

  • test/models/retirement_test.rb
  • app/models/retirement.rb
📚 Learning: 2025-09-01T22:31:57.345Z
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。

Applied to files:

  • test/models/retirement_test.rb
  • app/models/retirement.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (2)
test/models/retirement_test.rb (1)

23-23: LGTM!

メソッド名の更新が正しく反映されています。publish_to_newspaperからpublishへの変更により、ActiveSupport::Notificationsへの移行が適切にテストされています。

app/models/retirement.rb (1)

85-87: LGTM! ActiveSupport::Notificationsへの移行が適切に実装されています。

実装内容を確認しました:

  • イベント名'retirement.create'がActiveSupport::Notificationsの命名規則に従っています
  • ペイロードuser: @userが元のNewspaper.publishの呼び出しと一貫性を保っています
  • メソッド名publishは動詞であり、クラス名Retirementと冗長にならず、イベント発行の文脈で明確です

Retrieved learningsによると、この処理はUnfinishedDataDestroyerやTimesChannelDestroyerによる重要なデータクリーンアップを実行するため、退会のトリガーに関わらず必ず実行される必要があります。PR目的に記載されているように、サブスクライバーの動作については別途テストでカバーされていることを確認しました。

Based on learnings, this change correctly preserves the critical data cleanup behavior that must execute for all retirement triggers.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@matuaya matuaya changed the title retirement_createの処理をNewspaperからActiveSupport::Notificationsに移行 retirement_createとtraining_completion_createの呼び出しをNewspaperからActiveSupport::Notificationsに移行 Sep 30, 2025
@matuaya matuaya marked this pull request as ready for review September 30, 2025 15:35
@github-actions github-actions bot requested a review from komagata September 30, 2025 15:35
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a069234 and 3d3caa5.

📒 Files selected for processing (9)
  • app/controllers/admin/users_controller.rb (1 hunks)
  • app/controllers/retirement_controller.rb (1 hunks)
  • app/controllers/scheduler/daily/auto_retire_controller.rb (1 hunks)
  • app/controllers/training_completion_controller.rb (1 hunks)
  • app/models/times_channel_destroyer.rb (1 hunks)
  • app/models/unfinished_data_destroyer.rb (1 hunks)
  • config/initializers/active_support_notifications.rb (1 hunks)
  • config/initializers/newspaper.rb (0 hunks)
  • test/models/times_channel_destroyer_test.rb (2 hunks)
💤 Files with no reviewable changes (1)
  • config/initializers/newspaper.rb
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • app/models/times_channel_destroyer.rb
  • app/controllers/retirement_controller.rb
  • app/controllers/admin/users_controller.rb
  • test/models/times_channel_destroyer_test.rb
  • app/controllers/training_completion_controller.rb
  • config/initializers/active_support_notifications.rb
  • app/models/unfinished_data_destroyer.rb
  • app/controllers/scheduler/daily/auto_retire_controller.rb
test/**/*

⚙️ CodeRabbit configuration file

test/**/*: # Test

  • TestCase名は英語で書く。
  • どうしても避けられない時以外にsystem testでsleepは使わない。

Unit Test

model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。

Files:

  • test/models/times_channel_destroyer_test.rb
🧠 Learnings (3)
📓 Common learnings
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。
📚 Learning: 2025-09-01T22:31:57.345Z
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。

Applied to files:

  • app/controllers/retirement_controller.rb
  • app/controllers/admin/users_controller.rb
  • app/controllers/training_completion_controller.rb
  • config/initializers/active_support_notifications.rb
  • app/controllers/scheduler/daily/auto_retire_controller.rb
📚 Learning: 2025-09-01T22:31:57.345Z
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。

Applied to files:

  • app/controllers/retirement_controller.rb
  • app/controllers/admin/users_controller.rb
  • test/models/times_channel_destroyer_test.rb
  • config/initializers/active_support_notifications.rb
  • app/controllers/scheduler/daily/auto_retire_controller.rb
🧬 Code graph analysis (3)
app/models/times_channel_destroyer.rb (1)
app/models/unfinished_data_destroyer.rb (2)
  • call (3-10)
  • call (4-9)
test/models/times_channel_destroyer_test.rb (2)
app/models/times_channel_destroyer.rb (2)
  • call (3-16)
  • call (4-15)
app/models/unfinished_data_destroyer.rb (2)
  • call (3-10)
  • call (4-9)
app/models/unfinished_data_destroyer.rb (1)
app/models/times_channel_destroyer.rb (2)
  • call (3-16)
  • call (4-15)
🔇 Additional comments (7)
app/models/times_channel_destroyer.rb (1)

4-4: LGTM! ActiveSupport::Notificationsのコールバック仕様に正しく適合しています。

メソッドシグネチャがActiveSupport::Notificationsの標準的な5引数形式(name, started, finished, id, payload)に更新されています。未使用パラメータに_プレフィックスが付けられており、コーディング規約に従っています。

app/controllers/admin/users_controller.rb (1)

38-38: LGTM! イベント発行がActiveSupport::Notificationsに正しく移行されています。

Newspaper.publishからActiveSupport::Notifications.instrumentへの置き換えが適切に行われています。イベント名もretirement_createからretirement.createに変更され、Rails標準の命名規則に従っています。Based on learnings

app/controllers/training_completion_controller.rb (1)

18-18: LGTM! 研修終了イベントがActiveSupport::Notificationsに正しく移行されています。

training_completion_createイベントがtraining_completion.createに更新され、新しいイベント発行メカニズムに適合しています。ペイロード構造も保持されており、問題ありません。

config/initializers/active_support_notifications.rb (1)

59-65: LGTM! サブスクライバー登録が正しく設定されています。

UnfinishedDataDestroyerTimesChannelDestroyerretirement.createtraining_completion.createの両イベントに購読登録されています。これにより、退会時および研修終了時の重要なデータクリーンアップ処理(未チェックProduct・WIP Report削除、Discord分報チャンネル削除など)が確実に実行されます。Based on learnings

app/controllers/scheduler/daily/auto_retire_controller.rb (1)

18-18: LGTM! 自動退会フローでもイベント発行が正しく移行されています。

休会後の自動退会処理においても、retirement.createイベントがActiveSupport::Notificationsで発行されるよう正しく更新されています。これにより、退会のトリガーに関わらずデータクリーンアップ処理が確実に実行されます。Based on learnings

test/models/times_channel_destroyer_test.rb (1)

15-15: テストがプロダクションコードの署名変更に正しく対応しています。

両方のテストケース(成功パスと失敗パス)で TimesChannelDestroyer#call の呼び出しが新しい署名 (nil, nil, nil, nil, { user: }) に更新されています。未使用のActiveSupport::Notificationsパラメータに nil を渡すのは、ユニットテストとして適切です。

テストロジックとアサーションは変更されておらず、引き続きDiscordチャンネル削除の成功・失敗両方のシナリオを検証しています。

Also applies to: 29-29

app/controllers/retirement_controller.rb (1)

17-17: retirement.create イベントサブスクライバー登録に問題ありません

  • config/initializers/active_support_notifications.rb にて retirement.create が UnfinishedDataDestroyer と TimesChannelDestroyer に購読登録されていることを確認しました
  • 旧イベント名 retirement_create への参照が残っていないか、rg -n "retirement_create" config などで config ディレクトリ全体を再検索してください


class UnfinishedDataDestroyer
def call(payload)
def call(_name, _started, _finished, _id, payload)
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

ActiveSupport::Notifications購読者の署名に正しく適合しています。

メソッド署名が ActiveSupport::Notifications のコールバック署名 (name, started, finished, id, payload) に更新されています。未使用の4つのパラメータにアンダースコアプレフィックスが付けられており、適切です。メソッド本体のロジック(payload[:user]の抽出とクリーンアップ処理)は変更されていません。

コーディングガイドラインに基づき、モデルメソッドには対応するユニットテストが必要です。以下のスクリプトを実行して、UnfinishedDataDestroyerのテストが存在し、新しい署名に対応していることを確認してください:


🏁 Script executed:

#!/bin/bash
# Description: Verify UnfinishedDataDestroyer tests exist and match new signature

echo "=== Searching for UnfinishedDataDestroyer test file ==="
fd -t f "unfinished_data_destroyer.*test" test/

echo -e "\n=== Checking test calls to UnfinishedDataDestroyer#call ==="
rg -n -A 2 "UnfinishedDataDestroyer.*\.call" test/ --type=ruby

Length of output: 116


以下のスクリプトで spec/ ディレクトリ内のテストファイルと UnfinishedDataDestroyer#call 呼び出しを確認してください。


🏁 Script executed:

#!/bin/bash
echo "=== specディレクトリでのテストファイル検索 ==="
fd -t f "unfinished_data_destroyer.*_spec\.rb" spec/

echo -e "\n=== specディレクトリでのUnfinishedDataDestroyer#call呼び出し検索 ==="
rg -n -A 2 "UnfinishedDataDestroyer.*\.call" spec/ --type=ruby

Length of output: 224


UnfinishedDataDestroyerに対応するテストが存在しません。
ActiveSupport::Notificationsのコールバック署名変更(call(name, started, finished, id, payload))を検証するユニットテストが test/ および spec/ 内に見当たりません。新しい署名に対応したテストを追加してください。

🤖 Prompt for AI Agents
In app/models/unfinished_data_destroyer.rb around line 4, there is no unit test
verifying the ActiveSupport::Notifications callback uses the new signature
call(name, started, finished, id, payload); add tests under test/ (Minitest)
and/or spec/ (RSpec) that subscribe or instrument the notification and assert
UnfinishedDataDestroyer#call is invoked with five arguments: name, started (a
Time), finished (a Time), id, and payload (a Hash). In the tests, use
ActiveSupport::Notifications.instrument to emit a sample event, stub or spy the
UnfinishedDataDestroyer instance (or use allow/expect_any_instance_of) to
capture the received args, and assert the arity and values/types match the new
signature.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

テストの追加しました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the test file exists and review its content
echo "=== Checking if test file exists ==="
fd -t f "unfinished_data_destroyer_test.rb" test/

echo -e "\n=== Content of the test file ==="
cat test/models/unfinished_data_destroyer_test.rb

Length of output: 659


@matuaya さん、テストの追加をありがとうございます!確認しました。

test/models/unfinished_data_destroyer_test.rb のテストは適切に実装されています:

新しい5パラメータ署名を正しく使用

  • call(nil, nil, nil, nil, payload) で ActiveSupport::Notifications のコールバック署名に対応

すべての処理を検証

  • WIP日報の削除
  • 未チェック提出物の削除
  • career_path のリセット

テストケースが網羅的で、実装の変更を適切に検証しています。


🧬 Code Graph Analysis Results

<possibly_relevant_code_snippets>

app/controllers/reports_controller.rb (lines 24-30)

def unchecked
  return redirect_to reports_path unless admin_or_mentor_login?

  @reports = Report.list.page(params[:page]).per(PAGER_NUMBER)
  @reports = @reports.unchecked.not_wip
  render :index
end

app/models/times_channel_destroyer.rb (lines 3-16)

class TimesChannelDestroyer
  def call(_name, _started, _finished, _id, payload)
    user = payload[:user]
    return unless user.discord_profile.times_id

    if Discord::Server.delete_text_channel(user.discord_profile.times_id)
      user.discord_profile.times_id = nil
      user.discord_profile.times_url = nil
      user.discord_profile.save!(context: :retirement)
    else
      Rails.logger.warn "[Discord API] #{user.login_name}の分報チャンネルが削除できませんでした。"
    end
  end
end

🧠 Learnings used
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。

Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。

Learnt from: jun-kondo
PR: fjordllc/bootcamp#0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/work_notification_destroyer.rb`において、このクラスは`app/controllers/works_controller.rb`の`destroy`アクションから呼び出され、`before_action :set_my_work`でIDの妥当性チェックが事前に行われるため、payloadのworkオブジェクトは常に有効であることが保証されている。そのためエラーハンドリングは不要。

Learnt from: reckyy
PR: fjordllc/bootcamp#8356
File: app/models/notification.rb:45-47
Timestamp: 2025-07-15T12:58:02.735Z
Learning: fjordllc/bootcampプロジェクトの`app/models/notification.rb`において、`training_completed`通知種別は`TARGETS_TO_KINDS`ハッシュに含めない設計になっている。これは`training_completed`が通知一覧のフィルタ対象ではないためで、意図的な設計判断である。

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d3caa5 and 7430023.

📒 Files selected for processing (1)
  • test/models/unfinished_data_destroyer_test.rb (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • test/models/unfinished_data_destroyer_test.rb
test/**/*

⚙️ CodeRabbit configuration file

test/**/*: # Test

  • TestCase名は英語で書く。
  • どうしても避けられない時以外にsystem testでsleepは使わない。

Unit Test

model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。

Files:

  • test/models/unfinished_data_destroyer_test.rb
🧠 Learnings (1)
📓 Common learnings
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (3)
test/models/unfinished_data_destroyer_test.rb (3)

5-6: LGTM!

テストクラス名とテストケース名が適切です。コーディングガイドラインに従って英語で書かれています。


12-12: ActiveSupport::Notifications対応のシグネチャが正しく使用されています。

callメソッドの呼び出しが、ActiveSupport::Notificationsのサブスクライバー形式(name, started, finished, id, payload)に対応した新しいシグネチャで行われています。テスト内で未使用のパラメータにnilを渡すのは適切です。


7-9: フィクスチャの関連性は正しく設定されています
users(:sotugyou)report9wip: true の WIP日報)および product6(未チェック提出物)に紐づいていることを確認しました。

Comment on lines +13 to +14
assert_not Report.exists?(wip_report.id)
assert_not Product.exists?(unchecked_product.id)
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

WIP日報と未チェック提出物の削除が正しく検証されています。

アサーションはコアとなる動作(WIP日報と未チェック提出物の削除)を検証しており、適切です。

ただし、取得した学習内容によると、UnfinishedDataDestroyerはcareer_pathのリセットも行うとされています。このテストではcareer_pathの動作がカバーされていません。必要に応じて、career_pathのリセットも検証することを検討してください。

以下のスクリプトでUnfinishedDataDestroyerの実装を確認し、career_pathのリセット処理が含まれているか確認してください:


🏁 Script executed:

#!/bin/bash
# Description: Check UnfinishedDataDestroyer implementation for career_path reset

# Search for career_path in UnfinishedDataDestroyer
echo "=== UnfinishedDataDestroyer implementation ==="
rg -A 5 -B 5 'career_path' app/models/unfinished_data_destroyer.rb

# Also check the full implementation
echo "=== Full UnfinishedDataDestroyer class ==="
cat app/models/unfinished_data_destroyer.rb

Length of output: 596


テストにcareer_pathリセット検証を追加

test/models/unfinished_data_destroyer_test.rb に以下のアサーションを追記し、career_path が 0 にリセットされることを検証してください。

assert_equal 0, user.reload.career_path
🤖 Prompt for AI Agents
In test/models/unfinished_data_destroyer_test.rb around lines 13 to 14, add an
assertion to verify that the user's career_path is reset to 0 after destruction:
after the existing assertions (assert_not Report.exists?(wip_report.id) and
assert_not Product.exists?(unchecked_product.id)), reload the user and
assert_equal 0, user.reload.career_path so the test ensures career_path was
reset.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

追記しました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matuaya さん、ご対応ありがとうございます!テストにcareer_pathのリセット検証が追加され、UnfinishedDataDestroyerの動作が包括的にカバーされるようになりました。👍


🧠 Learnings used
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。

Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。

Learnt from: reckyy
PR: fjordllc/bootcamp#8356
File: app/models/notification.rb:45-47
Timestamp: 2025-07-15T12:58:02.735Z
Learning: fjordllc/bootcampプロジェクトの`app/models/notification.rb`において、`training_completed`通知種別は`TARGETS_TO_KINDS`ハッシュに含めない設計になっている。これは`training_completed`が通知一覧のフィルタ対象ではないためで、意図的な設計判断である。

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@matuaya matuaya force-pushed the chore/replace-newspaper-with-activesupport-notifications-for-retirement-create branch from 7430023 to f052caa Compare September 30, 2025 19:21
@matuaya matuaya force-pushed the chore/replace-newspaper-with-activesupport-notifications-for-retirement-create branch from f052caa to d4232af Compare October 6, 2025 08:28
@matuaya
Copy link
Copy Markdown
Contributor Author

matuaya commented Oct 6, 2025

@yokomaru
お疲れ様です😌
もしご負担でなければレビューをお願いできますでしょうか?🙇‍♀️

@matuaya matuaya requested a review from yokomaru October 6, 2025 09:08
@yokomaru
Copy link
Copy Markdown
Contributor

yokomaru commented Oct 6, 2025

@matuaya

matuayaさん、お疲れ様です!
承知しました〜!1週間ほどお時間いただけたらと思います🙏
よろしくお願いいたします🙌

@matuaya
Copy link
Copy Markdown
Contributor Author

matuaya commented Oct 6, 2025

@yokomaru
ありがとうございます!
よろしくお願いいたします😌🙏
全く急ぎではありませんので、いつでもご都合の良いタイミングでご確認いただけると嬉しいです☺️👍

Copy link
Copy Markdown
Contributor

@yokomaru yokomaru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matuaya

matuayaさん、お疲れ様です!
レビューに時間がかかってしまい、大変申し訳ありません🙇‍♀️

1点レビューいたしました&Descriptionについて1点だけコメントいたします!

  • 検証手順に foreman start -f Procfile.dev の実行を追加する
    • すでに foreman start -f Procfile.dev でアプリを立ち上げた状態から、別ブランチからchore/replace-newspaper-with-activesupport-notifications-for-retirement-createに切り替えただけだとWIPの日報が削除されませんでした👀
    • 詳細は追いきれなかったのですが、Rails起動時にconfig/initializers/active_support_notifications.rb を元にイベント購読(subscribe)の設定が行われるようなので、ブランチ切り替え後は再度 foreman start -f Procfile.dev を実行する旨を手順に追記した方が良いかなと思いました💡

全体的に非常に丁寧で読みやすく、検証しやすいPRでした〜!
今後自分がPRを作成する際に参考にさせていただきます🙌

お手数をおかけしますが、ご確認よろしくお願いいたします!

Comment on lines +10 to +16
wip_report = reports(:report9)
unchecked_product = products(:product6)
payload = { user: }

UnfinishedDataDestroyer.new.call(nil, nil, nil, nil, payload)
assert_not Report.exists?(wip_report.id)
assert_not Product.exists?(unchecked_product.id)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnfinishedDataDestroyerはユーザーの全てのWIPの日報と未チェックの提出物を削除するので、テストに関しても全てのWIPの日報と未チェックの提出物が削除されたことを確認できた方が実装が担保できそうだと思いました!💡

fixtureの追加 or テスト内で日報と提出物を複数create して、UnfinishedDataDestroyerをコールした後、
ユーザーに紐づくWIPの日報と未チェックの提出物が0件になっていることが確認できたら良いのかな?と思いました🫡

(加えて上記以外の日報と提出物は消えないこと、他のユーザーの日報と提出物は残ることまで検証できたらさらに安全かなと思いますが、最低限↑が確認できていれば問題ないかなと思います!)

@matuaya matuaya force-pushed the chore/replace-newspaper-with-activesupport-notifications-for-retirement-create branch from d4232af to 798c9f3 Compare October 17, 2025 10:28
@matuaya
Copy link
Copy Markdown
Contributor Author

matuaya commented Oct 17, 2025

@yokomaru
レビューありがとうございました☺️🙏
以下を修正しましたのでご確認いただけると嬉しいです!

  • 説明欄の「変更確認方法」に「foreman start -f Procfile.devを起動する」という手順を追加しました。
  • テストに以下を追加しました。
    • 「ユーザーの全てのWIP日報を未チェックの提出物は削除するが、他のユーザーのWIP日報と未チェック提出物は削除しない」
    • 「ユーザーのWIPではない日報とチェック済みの提出物は削除しない」

よろしくお願いいたします🙇‍♀️

レビューに時間がかかってしまい、大変申し訳ありません🙇‍♀️

全然急ぎではなかったし全く問題ないので大丈夫です☺️👌

Copy link
Copy Markdown
Contributor

@yokomaru yokomaru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matuaya

お疲れ様です!また少し時間空いてしまいすみません🙇‍♀️
Description修正やテストケースの追加など丁寧にご対応いただきありがとうございます!🙌

UnfinishedDataDestroyerテストに関してはCodeRabitの指摘にて不足していたところを追加いただいている部分だと思うので、今回のPRの本質的な部分ではないかもしれないのですが、いくつかコメントさせていただきました!(確認のお時間いただく形となってしまう点については申し訳ありません💦)

現在のテストでも動作自体は担保いただいていると思うのと、
自分もテストケースの作成やテスト設計に関して自信があるわけではないので、
違和感を感じた部分や伝わりづらい部分などがありましたら遠慮なくコメントいただければと思います!🙏

また、下記のように言っていただきとても安心しました・・(今まで軽微な修正のレビューのみだったので取り組むのにも時間がかかってしまい、お時間いただいてしまいました💦)
色々お気遣いいただき本当にありがとうございます🙏

全然急ぎではなかったし全く問題ないので大丈夫です☺️👌

どうぞよろしくお願いいたします!

Comment on lines +6 to +27
setup do
@user = users(:kimura)
@reports = 3.times.map do |i|
Report.create!(
user: @user,
title: "wipの日報#{i}",
description: 'テスト日報',
wip: true, reported_on: Date.current - i
)
end
@payload = { user: @user }
end
test '#call' do
@user.update(career_path: 1)
product = products(:product5)
report = @reports.first

UnfinishedDataDestroyer.new.call(nil, nil, nil, nil, @payload)
assert_not Report.exists?(report.id)
assert_not Product.exists?(product.id)
assert_equal 'unset', @user.reload.career_path
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnfinishedDataDestroyerの主要な責務は次の3点という認識なので、
#callのテストで全件(複数件)削除も含めて網羅されていると、テストを読んだときに処理内容をより直感的に理解できそうだなと思いました 💡

  1. ユーザーに紐づくWIPの日報を全削除する
  2. ユーザーに紐づく未チェックの提出物を全削除する
  3. ステータス(career_path)の更新

現在、exists?で特定の1件分の日報や提出物の存在チェックをしてくださっていると思うのですが、
#callのテストとしては全件削除できていることを確認できた方が上記の動作を明確に担保できるかなと思いました!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#callのテストで、WIP日報と未チェック提出物の全件削除を確認するように変更しました!
また、uncheckwipスコープを使用し、assertexist?を確認するのではなくcountを確認することで全件削除が明確に分かるようにしました🙏

Comment on lines +38 to +39
assert_not Product.where(id: user_unchecked_products).exists?
assert_not Report.where(id: user_wip_reports).exists?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#callの方で全件削除テストを追加いただければ、2つ目のテストでは「他ユーザーのデータが残っていること」だけ確認できればOKそうだと思いましたっ🙆‍♂️

Comment on lines +46 to +47
product = products(:product2)
report = reports(:report24)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:product2:report24がどのデータを指しているかが、ぱっと見だと理解しづらいように感じたので、
変数名に情報を追加いただくか、コメントで何のデータがわかると良いのかなと思いました💡

assert Report.exists?(other_wip_report.id)
end

test 'does not delete checked product or unwip report' do
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あまり英語に自信はないのですが、否定系のun-は形容詞など状態に関するものにつけるイメージがあり、
WIP(Work In Progress)という名詞につける場合はnon-が自然かな?と思いました💡(non-wip report)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご指摘の通り違和感ありますね💦
nonに変更しました!

@payload = { user: @user }
end
test '#call' do
@user.update(career_path: 1)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

user.update(career_path: 0)update!にしておくと更新に失敗した場合も気づけて安心かなと思いました💡

user: @user,
title: "wipの日報#{i}",
description: 'テスト日報',
wip: true, reported_on: Date.current - i
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Date.current - i で日付の操作ができるのを知らなかったのでとても勉強になりましたっ!
ループでいい感じにテストデータを作成できるのもとても良いと思ったので参考にさせていただきますっ

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

そう言っていただき嬉しいです☺️

@matuaya matuaya force-pushed the chore/replace-newspaper-with-activesupport-notifications-for-retirement-create branch from 798c9f3 to d346014 Compare October 22, 2025 08:18
@matuaya
Copy link
Copy Markdown
Contributor Author

matuaya commented Oct 22, 2025

@yokomaru
ご指摘ありがとうございます🙏
以下を修正したので確認お願いいたします!

  • callに全件削除のテストを追加
  • 変数名の変更
  • update!に修正

自分もテストケースの作成やテスト設計に関して自信があるわけではないので

私もテスト作成にあまり自信がなく、そして知識も乏しいので色々ご指摘いただけてとても嬉しいです☺️🙏

また少し時間空いてしまいすみません🙇‍♀️

全然気にしないでください!いつでもご都合のよいタイミングで大丈夫です☺️👍

@matuaya matuaya force-pushed the chore/replace-newspaper-with-activesupport-notifications-for-retirement-create branch from d346014 to 5d3ab16 Compare October 22, 2025 09:39
Copy link
Copy Markdown
Contributor

@yokomaru yokomaru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matuaya
お疲れ様です!
お待たせしてすみません!諸々ご対応いただき本当にありがとうございます!🙌
コミットが丁寧で修正が追いやすく、とても参考になりましたーー!

2点だけコメントさせていただきました!(何度もやり取りすみません・・🙇‍♀️)
こちらも違和感があったり伝わりづらかったら遠慮なくコメントくださいませ!🙏

お手隙でご確認の程よろしくお願いいたしいます!

class UnfinishedDataDestroyerTest < ActiveSupport::TestCase
setup do
@user = users(:kimura)
@reports = 3.times.map do |i|
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@reportsは後のコードで使っていないようなので削除して問題ないかなと思いました💡🙌


UnfinishedDataDestroyer.new.call(nil, nil, nil, nil, @payload)

assert_equal 0, Product.unchecked.where(user: @user).count
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WIPの日報の方はsetupで明示的にデータを作成しているためUnfinishedDataDestroyer実行後に0件であるというテストがわかりやすいのですが、
提出物の方はfixturesのデータに依存しているので未チェックの提出物のデータが存在しているのかがテストを見ただけだとパッと分からないのと、
(あまりないと思いますが)何かしらの理由でfixturesの未チェックのデータが削除・変更された場合でもテストが通ってしまう可能性があるなと思いました…!
自分が件数のチェックが良いと提案しておきながら考慮漏れしていて申し訳ありません🙏🙏🙏

上記にあたり、以下のどちらかの対応があるといいかな?と思いました!

  1. Reportと同様にProductもsetup内で作成する
  2. UnfinishedDataDestroyer実行前に未チェックの提出物が存在することを確認する(fixturesに未チェックの提出物があることを活かす)

個人的には 1 がいいかなと思っています!
(bootcampアプリの方針がどうかがわからないのですが)特定のテストでのみ更新・削除などで動的に使いたいデータはテスト内で明示的に作成する方が意図が伝わりやすく影響も受けにくいという認識です💡

フィクスチャを最適に管理するには、よくあるテストケースに適用可能なデフォルトデータのみを用いることです。
https://railsguides.jp/v7.0/testing.html#%E3%83%95%E3%82%A3%E3%82%AF%E3%82%B9%E3%83%81%E3%83%A3%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B

テストデータを fixture で用意すべきかどうか?
基本的になるべくテスト内で作りたいです
https://bootcamp.fjord.jp/reports/89239#comment_157960
※ こちらは新規作成の文脈ですが、今回のケース(削除の前提データ)にも考え方として近いかなと思いました!

@matuaya matuaya force-pushed the chore/replace-newspaper-with-activesupport-notifications-for-retirement-create branch from 935ef8e to 3e37413 Compare October 29, 2025 00:13
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
test/models/unfinished_data_destroyer_test.rb (1)

41-59: fixtureへの依存を減らすとテストがより明確になります

Test 2とTest 3はfixtureに依存していますが、Test 1のようにテスト内でデータを明示的に作成すると、以下のメリットがあります:

  • テストコードだけでデータの状態(他ユーザーのWIP日報、チェック済み提出物など)が分かる
  • fixtureの変更の影響を受けにくい
  • テストの意図がより明確になる

例えば、Test 2では以下のようにデータを作成できます:

test 'does not delete other users wip report and unchecked product' do
  other_user = users(:hatsuno)
  other_user_wip_report = Report.create!(
    user: other_user,
    title: '他ユーザーのWIP日報',
    description: 'テスト',
    wip: true,
    reported_on: Date.current
  )
  
  other_practice = Practice.where.not(id: other_user.products.pluck(:practice_id)).first
  other_user_unchecked_product = Product.create!(
    user: other_user,
    practice: other_practice,
    body: '提出物',
    wip: false
  )

  UnfinishedDataDestroyer.new.call(nil, nil, nil, nil, @payload)

  assert Product.exists?(other_user_unchecked_product.id)
  assert Report.exists?(other_user_wip_report.id)
end
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 935ef8e and 3e37413.

📒 Files selected for processing (1)
  • test/models/unfinished_data_destroyer_test.rb (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • test/models/unfinished_data_destroyer_test.rb
test/**/*

⚙️ CodeRabbit configuration file

test/**/*: # Test

  • TestCase名は英語で書く。
  • どうしても避けられない時以外にsystem testでsleepは使わない。

Unit Test

model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。

Files:

  • test/models/unfinished_data_destroyer_test.rb
🧠 Learnings (3)
📓 Common learnings
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。
📚 Learning: 2025-09-01T22:31:57.345Z
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。

Applied to files:

  • test/models/unfinished_data_destroyer_test.rb
📚 Learning: 2025-09-01T22:31:57.345Z
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。

Applied to files:

  • test/models/unfinished_data_destroyer_test.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (1)
test/models/unfinished_data_destroyer_test.rb (1)

10-39: 主要な責務を網羅した優れたテストです!

以下の点が正しく検証されています:

  • 対象ユーザーの全WIP日報が削除されること(Report.wip.where(user:).countが0)
  • 対象ユーザーの全未チェック提出物が削除されること(Product.unchecked.where(user:).countが0)
  • career_pathが'unset'にリセットされること

テスト内でデータを明示的に作成しているため、テストの意図が明確で、fixtureの変更の影響も受けにくい良い実装です。

@matuaya
Copy link
Copy Markdown
Contributor Author

matuaya commented Oct 29, 2025

@yokomaru
毎回修正が不十分で申し訳ありません🙇‍♀️
以下を修正したのでご確認お願いいたします。

  • Productをテスト内で作成(Product作成にあたってPracticeも用意が必要になりましたが、こちらはfixturesを使用しています。)
  • 不要なインスタンス変数を削除

よろしくお願いいたします🙏

Copy link
Copy Markdown
Contributor

@yokomaru yokomaru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matuaya

お疲れ様です!
いえいえ!!こちらこそ長い間、何度もやり取りのお手数をとらせてしまい申し訳ありません!🙏

ご対応いただきありがとうございました!こちらApproveさせていただきます!
rubyメソッドの使い分けやデータの取得方法などとても勉強になりました!!

引き続きよろしくお願いいたします!

Comment on lines +24 to +32
practices = Practice.where.not(id: user.products.pluck(:practice_id)).take(3)
practices.each do |practice|
Product.create!(
user:,
practice:,
body: '提出物',
wip: false
)
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where.not(...).take(3) で既存データとの重複を避けつつテストデータを生成しているのがとても綺麗&わかりやすくてすごく勉強になりました🙌
今後必要なデータを取得する際に参考にさせていただきます!!


class UnfinishedDataDestroyerTest < ActiveSupport::TestCase
setup do
@payload = { user: users(:kimura) }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setupがシンプルで、@payloadが外部から渡ってくるデータというのがわかりやすくて良いなと思いました!🙌

@matuaya
Copy link
Copy Markdown
Contributor Author

matuaya commented Oct 29, 2025

@yokomaru
最後まで丁寧にレビューしていただき本当にありがとうございました!🙏
提案や参考サイトなどを丁寧に共有してくださり、とても分かりやすく嬉しかったです☺️🙌

@komagata
チームメンバーのレビューが完了しましたのでご確認お願いいたします🙇‍♀️

@komagata
Copy link
Copy Markdown
Member

komagata commented Nov 4, 2025

@matuaya conflictの修正をお願いします〜。

@matuaya matuaya force-pushed the chore/replace-newspaper-with-activesupport-notifications-for-retirement-create branch from 3e37413 to a139940 Compare November 7, 2025 01:01
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/models/unfinished_data_destroyer.rb (1)

8-8: updateではなくupdate!の使用を検討してください。

退会時の重要なクリーンアップ処理において、user.update(career_path: 0)はバリデーションエラーが発生しても例外を発生させません。データの整合性を保証するため、user.update!(career_path: 0)を使用することを推奨します。

修正案:

-    user.update(career_path: 0)
+    user.update!(career_path: 0)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e37413 and a139940.

📒 Files selected for processing (8)
  • app/controllers/training_completion_controller.rb (1 hunks)
  • app/models/retirement.rb (2 hunks)
  • app/models/times_channel_destroyer.rb (1 hunks)
  • app/models/unfinished_data_destroyer.rb (1 hunks)
  • config/initializers/active_support_notifications.rb (1 hunks)
  • config/initializers/newspaper.rb (0 hunks)
  • test/models/times_channel_destroyer_test.rb (2 hunks)
  • test/models/unfinished_data_destroyer_test.rb (1 hunks)
💤 Files with no reviewable changes (1)
  • config/initializers/newspaper.rb
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/models/unfinished_data_destroyer_test.rb
  • app/controllers/training_completion_controller.rb
  • app/models/times_channel_destroyer.rb
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • test/models/times_channel_destroyer_test.rb
  • config/initializers/active_support_notifications.rb
  • app/models/unfinished_data_destroyer.rb
  • app/models/retirement.rb
test/**/*

⚙️ CodeRabbit configuration file

test/**/*: # Test

  • TestCase名は英語で書く。
  • どうしても避けられない時以外にsystem testでsleepは使わない。

Unit Test

model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。

Files:

  • test/models/times_channel_destroyer_test.rb
🧠 Learnings (10)
📓 Common learnings
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:131-135
Timestamp: 2025-09-12T21:18:00.834Z
Learning: Rails アップグレードPRにおいて、product_review_not_completed メソッドの webhook URL 設定の shared フォールバック追加も、設定システム全体の変更として別PRで対応すべきである。
Learnt from: reckyy
Repo: fjordllc/bootcamp PR: 8356
File: app/models/notification.rb:45-47
Timestamp: 2025-07-15T12:58:02.735Z
Learning: fjordllc/bootcampプロジェクトの`app/models/notification.rb`において、`training_completed`通知種別は`TARGETS_TO_KINDS`ハッシュに含めない設計になっている。これは`training_completed`が通知一覧のフィルタ対象ではないためで、意図的な設計判断である。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/models/graduation_notifier.rb:23-28
Timestamp: 2025-09-12T21:16:47.639Z
Learning: Rails upgrade PRにおいて、configuration systemの変更やwebhook設定の改善提案も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:52-56
Timestamp: 2025-09-12T21:17:45.295Z
Learning: Rails upgrade PRにおいて、configuration systemの改善提案(shared configuration fallback等)も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。
Learnt from: reckyy
Repo: fjordllc/bootcamp PR: 8356
File: db/fixtures/users.yml:823-826
Timestamp: 2025-07-15T12:33:04.885Z
Learning: fjordllc/bootcampプロジェクトでは、`trainee: true`は「研修に所属していたユーザー」全体を示すフラグであり、研修中のユーザーと研修を終了したユーザーの両方を含む。そのため、`trainee: true`と`training_completed_at`の併存は意図された仕様であり、排他的な関係ではない。
Learnt from: hirokiej
Repo: fjordllc/bootcamp PR: 8740
File: app/helpers/reports_helper.rb:56-64
Timestamp: 2025-06-29T03:44:15.179Z
Learning: このプロジェクト(fjordllc/bootcamp)では、ja.ymlファイルで一部の単語や文章のみI18n対応されているが、多くのテキストは日本語でハードコーディングされており、完全な国際化対応は行われていない。新しい機能でもI18n対応は不要と判断される。
📚 Learning: 2025-09-01T22:31:57.345Z
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。

Applied to files:

  • test/models/times_channel_destroyer_test.rb
  • config/initializers/active_support_notifications.rb
  • app/models/unfinished_data_destroyer.rb
  • app/models/retirement.rb
📚 Learning: 2025-09-01T22:31:57.345Z
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。

Applied to files:

  • test/models/times_channel_destroyer_test.rb
  • config/initializers/active_support_notifications.rb
  • app/models/unfinished_data_destroyer.rb
  • app/models/retirement.rb
📚 Learning: 2025-07-15T12:58:02.735Z
Learnt from: reckyy
Repo: fjordllc/bootcamp PR: 8356
File: app/models/notification.rb:45-47
Timestamp: 2025-07-15T12:58:02.735Z
Learning: fjordllc/bootcampプロジェクトの`app/models/notification.rb`において、`training_completed`通知種別は`TARGETS_TO_KINDS`ハッシュに含めない設計になっている。これは`training_completed`が通知一覧のフィルタ対象ではないためで、意図的な設計判断である。

Applied to files:

  • config/initializers/active_support_notifications.rb
  • app/models/unfinished_data_destroyer.rb
📚 Learning: 2025-07-23T21:11:21.826Z
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/work_notification_destroyer.rb`において、このクラスは`app/controllers/works_controller.rb`の`destroy`アクションから呼び出され、`before_action :set_my_work`でIDの妥当性チェックが事前に行われるため、payloadのworkオブジェクトは常に有効であることが保証されている。そのためエラーハンドリングは不要。

Applied to files:

  • app/models/unfinished_data_destroyer.rb
📚 Learning: 2025-07-23T21:11:21.826Z
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/inquiry_notifier.rb`において、`NOTIFICATION_SENDER_NAME`で指定される管理者ユーザーは必ず存在することがシステムの前提条件として保証されているため、senderがnilになることは想定されておらず、エラーハンドリングは不要である。

Applied to files:

  • app/models/unfinished_data_destroyer.rb
📚 Learning: 2025-09-12T21:18:00.834Z
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:131-135
Timestamp: 2025-09-12T21:18:00.834Z
Learning: Rails アップグレードPRにおいて、product_review_not_completed メソッドの webhook URL 設定の shared フォールバック追加も、設定システム全体の変更として別PRで対応すべきである。

Applied to files:

  • app/models/unfinished_data_destroyer.rb
📚 Learning: 2025-10-22T06:04:36.036Z
Learnt from: sjabcdefin
Repo: fjordllc/bootcamp PR: 9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.036Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。

Applied to files:

  • app/models/unfinished_data_destroyer.rb
📚 Learning: 2025-09-12T21:17:26.029Z
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:19-23
Timestamp: 2025-09-12T21:17:26.029Z
Learning: Rails アップグレードPRにおいて、config_for(:secrets) を使った webhook URL 設定の shared セクション未参照問題は、設定システム全体の変更として別PRで対応すべきである。

Applied to files:

  • app/models/unfinished_data_destroyer.rb
📚 Learning: 2025-09-12T21:16:47.639Z
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/models/graduation_notifier.rb:23-28
Timestamp: 2025-09-12T21:16:47.639Z
Learning: Rails upgrade PRにおいて、configuration systemの変更やwebhook設定の改善提案も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。

Applied to files:

  • app/models/unfinished_data_destroyer.rb
🧬 Code graph analysis (2)
test/models/times_channel_destroyer_test.rb (2)
app/models/times_channel_destroyer.rb (2)
  • call (3-16)
  • call (4-15)
app/models/unfinished_data_destroyer.rb (2)
  • call (3-10)
  • call (4-9)
app/models/unfinished_data_destroyer.rb (1)
app/models/times_channel_destroyer.rb (2)
  • call (3-16)
  • call (4-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (4)
test/models/times_channel_destroyer_test.rb (1)

15-15: LGTM!ActiveSupport::Notificationsのコールバック署名に正しく対応しています。

TimesChannelDestroyer#callの新しい5引数署名(name, started, finished, id, payload)に合わせて、テストが正しく更新されています。成功時と失敗時の両方のケースが適切にカバーされています。

Also applies to: 29-29

app/models/retirement.rb (1)

85-87: LGTM!ActiveSupport::Notificationsへの移行が適切に実装されています。

Newspaper.publishからActiveSupport::Notifications.instrumentへの移行が正しく行われています。イベント名とペイロード構造も適切で、トランザクション外で呼び出されているため副作用の処理も問題ありません。

config/initializers/active_support_notifications.rb (1)

64-70: LGTM!購読者の登録が適切に実装されています。

UnfinishedDataDestroyerTimesChannelDestroyerの購読者登録が正しく実装されています。両方ともretirement.createtraining_completion.createイベントに購読しており、既存の購読者パターンと一貫性があります。インスタンスの再利用も効率的です。

app/models/unfinished_data_destroyer.rb (1)

4-4: ActiveSupport::Notificationsのコールバック署名に正しく対応しています。

メソッド署名がActiveSupport::Notificationsのコールバック形式(name, started, finished, id, payload)に更新されています。未使用のパラメータに適切にアンダースコアプレフィックスが付けられています。

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
test/models/retirement_test.rb (1)

23-23: メソッド名の変更が適切に反映されています。

publish_to_newspaper から instrument_create_event への変更は、Newspaper gemからActiveSupport::Notificationsへの移行に対応しており、テストの更新として適切です。

オプションとして、ActiveSupport::Notifications.instrumentが正しいパラメータで呼び出されることを検証する専用のテストケースの追加も検討できます。例えば:

test 'instrument_create_event publishes retirement.create event with user' do
  retirement = Retirement.by_self({}, user: @user)
  
  assert_called_with(
    ActiveSupport::Notifications,
    :instrument,
    ['retirement.create', { user: @user }]
  ) do
    retirement.send(:instrument_create_event)
  end
end

ただし、イベントリスナ(UnfinishedDataDestroyer、TimesChannelDestroyer)のテストで実際のイベント処理が検証されている場合は、このレベルのテストは不要かもしれません。

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a139940 and d5df3c6.

📒 Files selected for processing (1)
  • test/models/retirement_test.rb (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • test/models/retirement_test.rb
test/**/*

⚙️ CodeRabbit configuration file

test/**/*: # Test

  • TestCase名は英語で書く。
  • どうしても避けられない時以外にsystem testでsleepは使わない。

Unit Test

model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。

Files:

  • test/models/retirement_test.rb
🧠 Learnings (2)
📓 Common learnings
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:131-135
Timestamp: 2025-09-12T21:18:00.834Z
Learning: Rails アップグレードPRにおいて、product_review_not_completed メソッドの webhook URL 設定の shared フォールバック追加も、設定システム全体の変更として別PRで対応すべきである。
Learnt from: reckyy
Repo: fjordllc/bootcamp PR: 8356
File: app/models/notification.rb:45-47
Timestamp: 2025-07-15T12:58:02.735Z
Learning: fjordllc/bootcampプロジェクトの`app/models/notification.rb`において、`training_completed`通知種別は`TARGETS_TO_KINDS`ハッシュに含めない設計になっている。これは`training_completed`が通知一覧のフィルタ対象ではないためで、意図的な設計判断である。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/models/graduation_notifier.rb:23-28
Timestamp: 2025-09-12T21:16:47.639Z
Learning: Rails upgrade PRにおいて、configuration systemの変更やwebhook設定の改善提案も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:52-56
Timestamp: 2025-09-12T21:17:45.295Z
Learning: Rails upgrade PRにおいて、configuration systemの改善提案(shared configuration fallback等)も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。
Learnt from: reckyy
Repo: fjordllc/bootcamp PR: 8356
File: db/fixtures/users.yml:823-826
Timestamp: 2025-07-15T12:33:04.885Z
Learning: fjordllc/bootcampプロジェクトでは、`trainee: true`は「研修に所属していたユーザー」全体を示すフラグであり、研修中のユーザーと研修を終了したユーザーの両方を含む。そのため、`trainee: true`と`training_completed_at`の併存は意図された仕様であり、排他的な関係ではない。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:19-23
Timestamp: 2025-09-12T21:17:26.029Z
Learning: Rails アップグレードPRにおいて、config_for(:secrets) を使った webhook URL 設定の shared セクション未参照問題は、設定システム全体の変更として別PRで対応すべきである。
Learnt from: sjabcdefin
Repo: fjordllc/bootcamp PR: 9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.036Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/work_notification_destroyer.rb`において、このクラスは`app/controllers/works_controller.rb`の`destroy`アクションから呼び出され、`before_action :set_my_work`でIDの妥当性チェックが事前に行われるため、payloadのworkオブジェクトは常に有効であることが保証されている。そのためエラーハンドリングは不要。
📚 Learning: 2025-09-01T22:31:57.345Z
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。

Applied to files:

  • test/models/retirement_test.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test

@matuaya
Copy link
Copy Markdown
Contributor Author

matuaya commented Nov 7, 2025

@komagata
conflictの解消をしましたのでご確認お願いいたします🙏


def publish_to_newspaper
Newspaper.publish(:retirement_create, { user: @user })
def instrument_create_event
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

メソッドは基本動詞であるべきで、こちらだとちょっと意味が通らない用に思いました。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご指摘ありがとうございます!
メソッド名をtrigger_retirement_eventに変更しました🙇‍♀️

@matuaya matuaya force-pushed the chore/replace-newspaper-with-activesupport-notifications-for-retirement-create branch from d5df3c6 to 5632ec7 Compare December 18, 2025 19:23
@matuaya matuaya force-pushed the chore/replace-newspaper-with-activesupport-notifications-for-retirement-create branch from 5632ec7 to e1c9eab Compare December 18, 2025 19:34
@matuaya
Copy link
Copy Markdown
Contributor Author

matuaya commented Dec 18, 2025

@komagata
修正しましたのでご確認お願いいたします🙇‍♀️


def publish_to_newspaper
Newspaper.publish(:retirement_create, { user: @user })
def trigger_retirement_event
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

クラスとメソッドは クラス.メソッド主語.動詞のような文章になるイメージです。

クラスがRetirementでメソッドがtrigger_retirement_eventだと両方にretirementが入っているので「頭痛が痛い」のような冗長な文章になっている形です。

これらを踏まえて自然な文になるようにメソッド名を考えてみてください。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

アドバイスありがとうございます!
publishにメソッド名を変更しました🙇‍♀️

@matuaya
Copy link
Copy Markdown
Contributor Author

matuaya commented Dec 21, 2025

@komagata
修正しましたのでご確認いただけると嬉しいです🙇‍♀️

Copy link
Copy Markdown
Member

@komagata komagata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確認させていただきました。OKです〜🙆‍♂️

@komagata komagata merged commit c8b73cf into main Jan 6, 2026
8 checks passed
@komagata komagata deleted the chore/replace-newspaper-with-activesupport-notifications-for-retirement-create branch January 6, 2026 09:53
@github-actions github-actions bot mentioned this pull request Jan 6, 2026
15 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants